package com.skaz.dao.jpa.util;

import com.skaz.util.Casts;
import com.skaz.util.Reflections;
import com.skaz.util.Strings;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public final class Querys {

    public enum Type {
        SQL, HQL
    }

    private Querys() {
    }

    public static Query createQuery(Session session, String sql, Object obj, Type queryType, boolean cacheable) {
        Query q = null;
        if (queryType.equals(Type.SQL)) {
            q = session.createSQLQuery(sql);
        } else {
            q = session.createQuery(sql);
        }
        if (obj != null) {
            if (obj instanceof Map) {
                Map<String, Object> params = (Map<String, Object>) obj;
                for (Entry<String, Object> param : params.entrySet()) {
                    q.setParameter(param.getKey(), param.getValue());
                }
            } else {
                Object[] args = (Object[]) obj;
                for (int i = 0; i < args.length; i++) {
                    q.setParameter(i, args[i]);
                }
            }
        }
        q.setCacheable(cacheable);
        return q;
    }

    public static Query createSqlQuery(Session session, String sql, Object obj) {
        return createQuery(session, sql, obj, Type.SQL, false);
    }

    public static Query createSqlQuery(Session session, String sql, Object obj, boolean cacheable) {
        return createQuery(session, sql, obj, Type.SQL, cacheable);
    }

    public static Query createHqlQuery(Session session, String sql, Object obj) {
        return createQuery(session, sql, obj, Type.HQL, false);
    }

    public static Query createHqlQuery(Session session, String sql, Object obj, boolean cacheable) {
        return createQuery(session, sql, obj, Type.HQL, cacheable);
    }

    public static <E> Specification<E> parseMapQuery(final Class<E> entityClass, final Map filter, final Map like) {
        return (Specification<E>) (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (filter != null && !filter.isEmpty()) {
                Iterator its = filter.entrySet().iterator();
                while (its.hasNext()) {
                    Entry entry = (Entry) its.next();
                    String name = (String) entry.getKey();
                    String key = name.replaceAll("\\>|\\<|\\=|\\!|\\ |\\t", "");// 清除>,<,!,空格,\t
                    Object value = convertStringToFieldType(entityClass, key, entry.getValue());
                    boolean valueIsNull = value == null || Strings.isNullOrEmpty(value);
                    Path expression;
                    if (Strings.indexOfAny(name, ">=", "<=", ">", "<") > 0) {
                        // 过滤掉空值
                        if (valueIsNull) {
                            continue;
                        }
                        String valueString = String.valueOf(value);
                        if (name.indexOf(">=") > 0) {
                            expression = root.get(name.replace(">=", ""));
                            predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) value));
                            continue;
                        }
                        if (name.indexOf("<=") > 0) {
                            expression = root.get(name.replace("<=", ""));
                            predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) value));
                            continue;
                        }
                        if (name.indexOf(">") > 0) {
                            expression = root.get(name.replace(">", ""));
                            predicates.add(builder.greaterThan(expression, (Comparable) value));
                            continue;
                        }
                        if (name.indexOf("<") > 0) {
                            expression = root.get(name.replace("<", ""));
                            predicates.add(builder.lessThan(expression, (Comparable) value));
                            continue;
                        }
                    } else {
                        if (name.indexOf("!") > 0) {
                            expression = root.get(Strings.remove(name.trim(), "!", "="));
                            if (valueIsNull) {
                                predicates.add(builder.isNotNull(expression));
                            } else {
                                predicates.add(builder.notEqual(expression, value));
                            }
                            continue;
                        } else {
                            expression = root.get(name);
                            if (valueIsNull) {
                                predicates.add(builder.isNull(expression));
                            } else {
                                predicates.add(builder.equal(expression, value));
                            }
                            continue;
                        }
                    }
                }
            }
            if (like != null && !like.isEmpty()) {
                Iterator its = like.entrySet().iterator();
                while (its.hasNext()) {
                    Entry entry = (Entry) its.next();
                    String name = (String) entry.getKey();
                    String key = name.replaceAll("\\>|\\<|\\=|\\!|\\ |\\t", "");// 清除>,<,!,空格,\t
                    String value = (String) convertStringToFieldType(entityClass, key, entry.getValue());
                    if (value == null) {
                        continue;
                    } else {
                        if (Strings.isNotBlank(value)) {
                            Path expression = root.get(name);
                            if (Strings.isContains(value, "%")) {
                                predicates.add(builder.like(expression, value));
                            } else {
                                predicates.add(builder.like(expression, "%" + value + "%"));
                            }
                        }
                    }
                }
            }
            // 将所有条件用 and 联合起来
            if (predicates.size() > 0) {
                return builder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
            return builder.conjunction();
        };
    }

    /**
     * Jpa 动态查询条件组装
     * 复杂查询的封装 https://blog.csdn.net/u012706811/article/details/53218102
     */

    public static org.springframework.data.domain.Sort parseMapSort(Map<String, ?> sort) {
        org.springframework.data.domain.Sort sorter = org.springframework.data.domain.Sort.unsorted();
        List<org.springframework.data.domain.Sort> sorters = new ArrayList<>();
        if (sort != null && !sort.isEmpty()) {
            for (Entry<String, ?> entry : sort.entrySet()) {
                String name = entry.getKey();
                String value = entry.getValue().toString();
                if (Strings.isNullOrEmpty(value)) {
                    sorters.add(new org.springframework.data.domain.Sort(Direction.ASC, name));
                } else {
                    if (value.trim().toLowerCase().equals("asc")) {
                        sorters.add(new org.springframework.data.domain.Sort(Direction.ASC, name));
                    } else {
                        sorters.add(new org.springframework.data.domain.Sort(Direction.DESC, name));
                    }
                }
            }
            for (int i = 0; i < sorters.size(); i++) {
                sorter = sorter.and(sorters.get(i));
            }
        }
        return sorter;
    }


    private static Object convertStringToFieldType(Class<?> clazz, String field, Object value) {
        if (value instanceof String) {
            String value_string = String.valueOf(value).trim();
            Class<?> field_type = Reflections.getField(clazz, field).getType();
            value = Casts.to(value_string, field_type);
        }
        return value;
    }

}
